#ifndef TCP_HEADERS
#define TCP_HEADERS
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <errno.h>
#endif

#ifndef STRING_H
#define STRING_H
#include <string>
#endif

#ifndef RAWCONN_H
#define RAWCONN_H
#include "raw_connection.h"
#endif

#ifndef RAWLISTENER_H
#define RAWLISTENER_H
#include "raw_listener.h"
#endif

#ifndef SBCP_H
#define SBCP_H
#include "../high_level_packet/SBCP.h"
#endif

#ifndef packet_H
#define packet_H
#include "packet.h"
#endif

#ifndef flags_H
#define flags_H
#include "../packet_headers/flags.h"
#endif

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <map>

#define null 0
#define IO_BLOCK_SIZE 32
#define BUFFER_SIZE_CLI 512

/* RAW PACKET TYPES */
#define ICMP_ECHO 1
#define UDP_SNMP 2
#define UDP_R1 3
#define UDP_R2 4
#define TCP_PORT20 5
#define TCP_PORT21 6
#define TCP_PORT23 7
#define TCP_PORT80 8
#define TCP_P2P1 9
#define TCP_P2P2 10
#define TCP_R1 11
#define TCP_R2 12

#define RAW_TYPE_NUMBER 12
#define CLI_END_SIGNAL 13
using namespace std;

/* TCP Connection Implementation */
class TCPConnection {
	sockaddr_in host_to_addr(std::string _host, unsigned short _port);
public:
	int sock;
	sockaddr_in addr;
	unsigned short port;
	std::string ip;
	unsigned short cli_raw_port;
	unsigned short serv_raw_port;
	int burst_time;
	int round_time;
	int delta_sec;
	char serv_ip[256];
	char cli_ip[256];
	RawConnection ctcp_raw;
	RawConnection cudp_raw;
	RawConnection cicmp_raw;
	map<int, byte*> byte_list;
	map<int, byte*>::iterator byte_it;
	
	TCPConnection() { }
	TCPConnection(std::string _host, unsigned short _port, unsigned short _raw_port, 
			unsigned short _burst_time, unsigned short _round_time, unsigned short _delta_sec) {
		sock = socket(AF_INET, SOCK_STREAM, 0);
		port = _port;
		ip = _host;
		addr = host_to_addr(_host, _port);
		if(addr.sin_addr.s_addr == null) {
			// Failed to get host info
			sock = null;
		}
		else {
			int result = connect(sock, (sockaddr*)&addr, sizeof(sockaddr_in));			
			if (result < 0) {
				std::string err_msg = "";
				if(errno == ECONNREFUSED) {
					err_msg = "Connection refused.";
				}
				else if(errno == EISCONN) {
					err_msg = "Socket is already connected.";
				}
				else if(errno == ETIMEDOUT) {
					err_msg = "Connection timed out.";
				}
				else if(errno == EHOSTUNREACH) {
					err_msg = "No route to host.";
				}
				else if(errno == ENETUNREACH) {
					err_msg = "No route to network.";
				}
				else {
					printf("%d\n", errno);
					err_msg = "Could not connect to host.";
				}
				printf("Error: %s\n", err_msg.c_str());
				sock = 0;
			}
			cli_raw_port = _raw_port;
			burst_time = _burst_time;
			round_time = _round_time;
			delta_sec = _delta_sec;
		}
	}
	void handle_client_connection();
	int round_burst(RawConnection ctcp_raw, RawConnection cudp_raw, RawConnection cicmp_raw, 
			map<int, byte*> byte_list, map<int, byte*>::iterator byte_it);
	void close_connection();
	map<int, byte*> create_byte_list();
};

